//
// Created by denglibin on 2021/5/24.
//
#include <audio.h>
/**
 * 获取文件大小 字节数
 * @param file
 * @return
 */
static long getFileSize(const char* file){
    FILE * f = fopen(file, "rb");
    if(!f){
        av_log(NULL, AV_LOG_ERROR,"打开文件失败%s\n", file);
        exit(-1);
    }
    //置流 stream 的文件位置为给定的偏移 offset,第三个参数表示相对位置
    fseek(f,0, SEEK_END);
    long size = ftell(f);// 得到文件位置指针当前位置相对于文件首的偏移字节数
    fclose(f);
    return size;
}
/**
 * pcm编码为aac格式文件
 * @param pcmFile pcm文件
 */
int pcmToAAC(const char* pcmFile, const char* outFile){

    int ret;
    //找到AAC编码器
    AVCodec * aacEncoder = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if(!aacEncoder){
       av_log(NULL, AV_LOG_ERROR, "查找编码器aac失败\n");
       return -1;
    }
   //编码器上下文
    AVCodecContext *aacEncoderContext = avcodec_alloc_context3(aacEncoder);
    if(!aacEncoderContext){
        av_log(NULL, AV_LOG_ERROR, "avcodec_alloc_context3失败\n");
        return -1;
    }
    //编码器参数设置
    aacEncoderContext->sample_rate = 44100; //采样率
    aacEncoderContext->channels = 2; //声道数
    aacEncoderContext->channel_layout = AV_CH_LAYOUT_STEREO;

    aacEncoderContext->sample_fmt = AV_SAMPLE_FMT_FLTP;// 数据格式
    aacEncoderContext->bit_rate = 64000;

    aacEncoderContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    //打开编码器
    ret = avcodec_open2(aacEncoderContext, aacEncoder, NULL);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "avcodec_open2失败\n");
        return -1;
    }

    //创建一个输出的上下文
    AVFormatContext* outputContext = NULL;
    //初始化
    avformat_alloc_output_context2(&outputContext, NULL, NULL, outFile);
    if(!outputContext){
        av_log(NULL, AV_LOG_ERROR, "avformat_alloc_output_context2失败\n");
        return -1;
    }
    //输出流
    struct AVStream* outStream = avformat_new_stream(outputContext, NULL);
    outStream->codecpar->codec_tag = 0;

    //复制参数
    avcodec_parameters_from_context(outStream->codecpar, aacEncoderContext);
    av_dump_format(outputContext, 0, outFile, 1);

    //打开
    ret = avio_open(&outputContext->pb, outFile, AVIO_FLAG_WRITE);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "avio_open失败\n");
        return -1;
    }
    //写入头部信息
    ret = avformat_write_header(outputContext, NULL);
    if (ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "avformat_write_header失败");
        return -1;
    }

    //重采样
    //设置参数
    SwrContext * swrContext = swr_alloc_set_opts(NULL,
                       aacEncoderContext->channel_layout, aacEncoderContext->sample_fmt, aacEncoderContext->sample_rate, //输出信息
                       AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16,44100,  //输入信息
                       0,0);
    if(!swrContext){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "swr_alloc_set_opts失败");
        return -1;
    }
    //初始化
    ret = swr_init(swrContext);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "swr_init失败");
        return -1;
    }
    //帧(编码之前的数据)
    AVFrame * frame = av_frame_alloc();
    frame->format = AV_SAMPLE_FMT_FLTP; //样本格式
    frame->channels = 2; //通道数
    frame->channel_layout = AV_CH_LAYOUT_STEREO;
    frame->nb_samples = 1024; //每一帧样本数
    //创建缓冲区
    ret = av_frame_get_buffer(frame, 0);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "av_frame_get_buffer失败");
        return -1;
    }
    //一帧样本的大小
    int readSize = frame->nb_samples * 2 * 2;// 双声道，16位
    char* pcms = av_malloc(readSize);
    //输入文件
    FILE* inFile = fopen(pcmFile, "rb");
    unsigned long long len;
    //创建AVPacket
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    while (1){
        //读取输入文件数据
        len = fread(pcms, 1, readSize, inFile);
        if(len <= 0){
            break;
        }
        //重采样之前的数据
        const uint8_t *data[1]; //指针数组
        data[0] = (uint8_t *)pcms;
        swr_convert(swrContext, frame->data, frame->nb_samples, //重采样之后
                    data, frame->nb_samples); //重采样之前
        //发送给编码线程
        ret = avcodec_send_frame(aacEncoderContext, frame);
        if(ret < 0){
            continue;
        }
        //接收编码后的数据
        ret = avcodec_receive_packet(aacEncoderContext, packet);
        if(ret !=0 ){
            continue;
        }
        //0 表示音频流
        packet->stream_index = 0;
        packet->dts = 0;
        packet->pts = 0;
        //写到输出上下文
        ret = av_interleaved_write_frame(outputContext, packet);
        if(ret != 0){
            av_log(NULL, AV_LOG_ERROR, "%s\n", "av_interleaved_write_frame失败");
            break;
        }

    }
    av_free(pcms);
    pcms = NULL;
    fclose(inFile);
    av_write_trailer(outputContext);

    //关闭
    avio_close(outputContext->pb);
    //关闭编码器
    avcodec_close(aacEncoderContext);
    //清理设置的参数
    avcodec_free_context(&aacEncoderContext);

    //清理输出上下文
    avformat_free_context(outputContext);

    return 0;
}


/**
 * pcm 添加头信息转成wav
 * @param pcmFile
 * @param channels 通道数
 * @param sampleRate 采样率
 * @param wavFile
 * @return
 */
int pcmToWAV(const char* pcmFile, int channels, int sampleRate, const char* wavFile){
    long pcmFileSize = getFileSize(pcmFile);
    //av_log(NULL, AV_LOG_INFO, "pcm文件大小:%ld\n", pcmFileSize);
    FILE * pcmF = fopen(pcmFile, "rb");
    FILE * wavF = fopen(wavFile, "wb");
    //第一部分
    WavHeader header;
    if(!pcmFile){
        av_log(NULL, AV_LOG_ERROR, "打开文件失败%s\n", pcmFile);
        return -1;
    }
    if(!wavF){
        av_log(NULL, AV_LOG_ERROR, "打开文件失败%s\n", wavFile);
        return -1;
    }
    memcpy(header.chunkId, "RIFF", strlen("RIFF"));

    header.chunkSize = pcmFileSize + 44 - 8;
    memcpy(header.format, "WAVE", strlen("WAVE"));
    //第二部分
    WavFmt fmt;
    memcpy(fmt.subChunk1Id, "fmt ", strlen("fmt "));
    fmt.subChunk1Size = 16; //位宽 16位
    fmt.audioFormat = 1; // pcm标识
    fmt.channels = channels; //通道数
    fmt.sampleRate = sampleRate; //采样率
    fmt.bitRate = sampleRate * channels * fmt.subChunk1Size / 8; //码率 (每秒传递位数)
    fmt.blockAlign = channels * 16 / 8;
    fmt.bitPerSample = 16; //样本格式 16位

    //第三部分
    WavData  data;
    memcpy(data.subChunk2Id, "data", strlen("data"));
    data.subChunk2Size = pcmFileSize;

    //写入wav信息44个字节
    fwrite(&header, sizeof(WavHeader), 1, wavF);
    fwrite(&fmt, sizeof(WavFmt), 1, wavF);
    fwrite(&data, sizeof(WavData), 1, wavF);

    //写入pcm数据
    char buf[1024];
    unsigned long long len;
    while ((len = fread(buf, sizeof(char),1024, pcmF)) != 0){
        fwrite(buf, sizeof(char), len, wavF);
    }
    fclose(wavF);
    fclose(pcmF);
    av_log(NULL, AV_LOG_INFO, "success");
}


/**
 * pcm编码为mp3格式文件
 * @param pcmFile
 * @param outFile
 * @return
 */
int pcmToMP3(const char* pcmFile, const char* outFile){
    int ret;
    //找到编码器
    AVCodec * mp3Encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if(!mp3Encoder){
        av_log(NULL, AV_LOG_ERROR, "查找编码器失败\n");
        return -1;
    }
    //编码器上下文
    AVCodecContext *mp3EncoderContext = avcodec_alloc_context3(mp3Encoder);
    if(!mp3EncoderContext){
        av_log(NULL, AV_LOG_ERROR, "avcodec_alloc_context3失败\n");
        return -1;
    }
    mp3EncoderContext->bit_rate = 64000;
    mp3EncoderContext->channels = 2;
    mp3EncoderContext->channel_layout = AV_CH_LAYOUT_STEREO;
    mp3EncoderContext->sample_rate = 44100;
    mp3EncoderContext->sample_fmt = AV_SAMPLE_FMT_S16P;

    //打开编码器
    ret = avcodec_open2(mp3EncoderContext, mp3Encoder, NULL);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "avcodec_open2失败\n");
        return -1;
    }
    //打开输出的文件
    FILE * outF = fopen(outFile, "wb");
    if(!outF){
        av_log(NULL, AV_LOG_ERROR, "fopen失败%s\n", outFile);
        return -1;
    }
    //帧空间
    AVFrame* frame = av_frame_alloc();
    if(!frame){
        av_log(NULL, AV_LOG_ERROR, "av_frame_alloc失败\n");
        return -1;
    }
    frame->format = AV_SAMPLE_FMT_S16P; //样本格式
    frame->channels = 2; //通道数
    frame->channel_layout = AV_CH_LAYOUT_STEREO;
    frame->nb_samples = 1152; //每一帧样本数

    //创建缓冲区
    ret = av_frame_get_buffer(frame, 0);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "av_frame_get_buffer失败");
        return -1;
    }

    //重采样
    //设置参数
    SwrContext * swrContext = swr_alloc();
    if(!swrContext){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "swr_alloc失败");
        return -1;
    }
    //设置重采样输入参数，通道布局立体声，采样率，样本格式不一致，输入s16交错存储，输出s16p平面存储
    av_opt_set_int(swrContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
    av_opt_set_int(swrContext, "in_sample_rate", 44100, 0);
    av_opt_set_sample_fmt(swrContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
    //设置重采样输出参数
    av_opt_set_int(swrContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
    av_opt_set_int(swrContext, "out_sample_rate", 44100, 0);
    av_opt_set_sample_fmt(swrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16P, 0);

    //初始化
    ret = swr_init(swrContext);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "swr_init失败");
        return -1;
    }
    FILE * pcmF = fopen(pcmFile, "rb");
    if(!pcmF){
        av_log(NULL, AV_LOG_ERROR, "fopen失败%s\n", pcmFile);
        return -1;
    }
    //存储从pcm文件读取过来的数据
    uint8_t ** input_data = NULL;
    //存储重采样之后的数据
    uint8_t ** out_data = NULL;
    int input_linesize, out_linesize;
    //分配空间
    av_samples_alloc_array_and_samples(&input_data, &input_linesize,
                                       2, 1152, AV_SAMPLE_FMT_S16, 0);

    av_samples_alloc_array_and_samples(&out_data, &out_linesize,
                                       2, 1152, AV_SAMPLE_FMT_S16P, 0);

    //创建AVPacket
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    if(!packet){
        av_log(NULL, AV_LOG_ERROR, "AVPacket av_malloc失败%s\n", pcmFile);
        return -1;
    }
    unsigned  long long readSize;
    while ((!feof(pcmF))){
        readSize = fread(input_data[0], 1, 1152 * 2 * 2, pcmF); //1152 * 2 * 2  每一帧样本数 通道数 16位 2个字节
        if(readSize == 0){
            break;
        }
        //重采样
        ret = swr_convert(swrContext, out_data,  1152, (const uint8_t**)input_data, 1152);
        if(ret < 0){
            av_log(NULL, AV_LOG_ERROR, "swr_convert失败%s\n", pcmFile);
            break;
        }
        frame->data[0] = out_data[0]; //左声道
        frame->data[1] = out_data[1]; //右声道
        //发送给编码线程
        ret = avcodec_send_frame(mp3EncoderContext, frame);
        if(ret < 0){
            continue;
        }
        while (ret >= 0){
            //接收编码后的数据
            ret = avcodec_receive_packet(mp3EncoderContext, packet);
            if(ret  == AVERROR(EAGAIN) || ret == AVERROR_EOF ){
                continue;
            }else if(ret < 0){
                break;
            }
            //写入到文件
            fwrite(packet->data, 1, packet->size, outF);
            av_packet_unref(packet);
        }

    }
    //释放资源
    av_freep(input_data);
    av_freep(out_data);

    fclose(pcmF);
    fclose(outF);

    av_frame_free(&frame);
    av_packet_free(&packet);

    swr_free(&swrContext);
    avcodec_free_context(&mp3EncoderContext);
    av_log(NULL, AV_LOG_INFO, "success\n");
}